package com.fit.spring.boot.proxy.extension.autoconfigure;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.aopalliance.aop.Advice;
import org.aopalliance.intercept.MethodInterceptor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.framework.DefaultAopProxyFactory;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;

import com.fit.spring.boot.proxy.extension.interceptor.Config;
import com.fit.spring.boot.proxy.extension.interceptor.InterceptorChain;
import com.fit.spring.boot.proxy.extension.interceptor.InterceptorConfiguration;
import com.fit.spring.boot.proxy.extension.interceptor.ProxyExtensionMethodInterceptor;
import com.fit.spring.boot.proxy.extension.utils.AopTargetUtils;

/**
 * 代理扩展自动启动配置类
 * @author hcl
 *
 */
@Import(ProxyExtensionProperties.class)
public class ProxyExtensionAutoConfiguration implements BeanPostProcessor{

	private static Logger log = LoggerFactory.getLogger(ProxyExtensionAutoConfiguration.class);
	
	/**
	 * 配置对象
	 */
	private ProxyExtensionProperties proxyExtensionProperties;
	
	/**
	 * 代理拦截器配置器
	 */
	private List<InterceptorConfiguration> interceptorConfigurations = new ArrayList<InterceptorConfiguration>();
	
	/**
	 * 构造方法初始化
	 * @param proxyExtensionProperties
	 * @param interceptorConfigurationMap
	 */
	public ProxyExtensionAutoConfiguration(
			ProxyExtensionProperties proxyExtensionProperties,
			ApplicationContext context){
		
		this.proxyExtensionProperties = proxyExtensionProperties;
		if(proxyExtensionProperties.getEnabled()){
			this.interceptorConfigurations.addAll(context.getBeansOfType(InterceptorConfiguration.class).values());
		}else{
			log.warn("Shut down the Proxy Extension! config attribute enabled is '{}'。", proxyExtensionProperties.getEnabled());
		}
	}
	
	/**
	 * bean初始化前置处理器
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	/**
	 * bean初始化后置处理器
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		if(proxyExtensionProperties.getEnabled()){
			Class<?> targetClass = getTargetClass(bean);
			Map<Method, InterceptorChain> interceptorChain = getAllInterceptor(targetClass);
			if(interceptorChain.size() > 0){
				return createProxy(bean, interceptorChain);
			}
		}
		return bean;
	}

	/**
	 * 获取方法拦截器链
	 * @param targetClass
	 * @return
	 */
	private Map<Method, InterceptorChain> getAllInterceptor(Class<?> targetClass){
		Map<Method, InterceptorChain> methodInterceptorChain = new HashMap<Method, InterceptorChain>();
		//获取bean拦截器
		List<MethodInterceptor> beanInterceptors = getBeanInterceptors(targetClass);
		//获取bean Method拦截器
		Method[] methods = targetClass.getMethods();
		for(Method method : methods){
			List<MethodInterceptor> interceptorsList = new ArrayList<MethodInterceptor>();
			interceptorsList.addAll(beanInterceptors);
			List<MethodInterceptor> beanMethodInterceptors = getBeanMethodInterceptors(targetClass,method);
			interceptorsList.addAll(beanMethodInterceptors);
			InterceptorChain interceptorChain = createInterceptorChain(interceptorsList);
			if(interceptorChain != null){
				methodInterceptorChain.put(method, interceptorChain);
			}
		}
		return methodInterceptorChain;
	}
	
	/**
	 * 获取bean的拦截器
	 * @return
	 */
	private List<MethodInterceptor> getBeanInterceptors(Class<?> beanClass){
		ArrayList<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>();
		for(InterceptorConfiguration ic : interceptorConfigurations){
			Config config = new Config(beanClass,null);
			ic.registeredBeanInterceptors(config);
			List<MethodInterceptor> interceptorList = config.getMethodInterceptors();
			if(interceptorList != null && interceptorList.size() > 0){
				interceptors.addAll(interceptorList);
			}
		}
		return interceptors;
	}
	
	/**
	 * 获取bean 方法的拦截器
	 * @return
	 */
	private List<MethodInterceptor> getBeanMethodInterceptors(Class<?> beanClass,Method method){
		ArrayList<MethodInterceptor> interceptors = new ArrayList<MethodInterceptor>();
		for(InterceptorConfiguration ic : interceptorConfigurations){
			Config config = new Config(beanClass,method);
			ic.registeredBeanMethodInterceptors(config);
			List<MethodInterceptor> interceptorList = config.getMethodInterceptors();
			if(interceptorList != null && interceptorList.size() > 0){
				interceptors.addAll(interceptorList);
			}
		}
		return interceptors;
	}
	
	/**
	 * 创建拦截器链
	 * @param interceptorsList
	 * @return
	 */
	private InterceptorChain createInterceptorChain(List<MethodInterceptor> interceptorsList){
		if(interceptorsList != null){
			interceptorsList.removeAll(Collections.singleton(null));
			if(interceptorsList.size() > 0){
				InterceptorChain firstInterceptorChain = new InterceptorChain();
				firstInterceptorChain.setMethodInterceptor(interceptorsList.get(0));
				if(interceptorsList.size() > 1){
					InterceptorChain interceptorChain = firstInterceptorChain;
					for(int i=1,len=interceptorsList.size(); i<len; i++){
						InterceptorChain chain = new InterceptorChain();
						chain.setMethodInterceptor(interceptorsList.get(i));
						interceptorChain.setNextInterceptorChain(chain);
						interceptorChain = chain;
					}
				}
				return firstInterceptorChain;
			}
		}
		return null;
	}
	
	/**
	 * 创建代理对象,并初始化代理对象拦截器
	 * @param bean
	 * @param methodInterceptorChain
	 * @return
	 */
	private Object createProxy(Object bean,Map<Method, InterceptorChain> methodInterceptorChain){
		if(methodInterceptorChain == null || methodInterceptorChain.size() == 0){
			return bean;
		}
		ProxyFactoryBean config = new ProxyFactoryBean();
		config.setTarget(bean);
		//创建通知
		Advice advice = new ProxyExtensionMethodInterceptor(methodInterceptorChain);
		config.addAdvice(advice);
		DefaultAopProxyFactory aopFactory = new DefaultAopProxyFactory();
		return aopFactory.createAopProxy(config).getProxy();
	}
	
	/**
	 * 获取目标class
	 * @param bean
	 * @return
	 */
	private static Class<?> getTargetClass(Object bean){
		try {
			return AopTargetUtils.getTarget(bean).getClass();
		} catch (Exception e) {
			throw new RuntimeException("获代理bean目标class异常！class = '" + bean.getClass().getName() + "'",e);
		}
	}
	
}
